package clock

import (
	"sort"
	"sync"
	"time"

	"github.com/HuKeping/rbtree"
)

const (
	exit = 0
	open = 1
)

//Clock 定时器
type Clock struct {
	update chan bool      //任务
	reset  chan chan bool //重置
	exit   chan chan bool //退出
	isExit uint32         //退出
	jobs   *JobQueue      //任务
	rbTree *rbtree.Rbtree //红黑树
	goWait sync.WaitGroup //等待退出
	lock   sync.Mutex     //锁
}

//CreateClock 创建一个定时器
func CreateClock() *Clock {
	clock := new(Clock)
	clock.update = make(chan bool)
	clock.reset = make(chan chan bool)
	clock.exit = make(chan chan bool)
	clock.isExit = open
	clock.jobs = NewJobQueue()
	clock.rbTree = rbtree.New()
	go clock.Start()
	return clock
}

//Start 开启一个定时器
func (pointer *Clock) Start() {
	for {
		//获取新的时间
		job := pointer.jobs.Min()
		if job == nil {
			job := new(Job)
			job.count = 0
			job.intervalTime = 9999 * time.Second
			job.executionTime = time.Now().Add(job.intervalTime)
			pointer.jobs.Insert(job)
			continue
		}
		//添加定时
		executionTime := job.executionTime.Sub(time.Now())
		select {
		case <-time.After(executionTime):
			if job == nil {
				break
			}
			if job.callBackFunc != nil {
				pointer.goWait.Add(1)
				go func() {
					defer pointer.goWait.Done()
					job.callBackFunc(job.param)
				}()
			}
			if job.count == 0 {
				job.executionTime = time.Now().Add(job.intervalTime)
				break
			}
			if job.count == 1 {
				pointer.jobs.Remove(job)
				break
			}
			job.count = job.count - 1
			job.executionTime = time.Now().Add(job.intervalTime)
			break
		case <-pointer.update:
			break
		case ch := <-pointer.reset:
			pointer.jobs.Clear()
			ch <- true
			break
		case ch := <-pointer.exit:
			pointer.jobs.Clear()
			pointer.goWait.Wait()
			ch <- true
			return
		}
	}
}

//AddClock 添加定时任务 纳秒级别
func (pointer *Clock) AddClock(intervalTime time.Duration, count int, function func(interface{}), param interface{}) (*Job, bool) {
	pointer.lock.Lock()
	if pointer.isExit > exit {
		job := new(Job)
		job.clock = pointer
		job.count = count
		job.intervalTime = intervalTime
		job.callBackFunc = function
		job.param = param
		job.executionTime = time.Now().Add(job.intervalTime)
		pointer.jobs.Insert(job)
		pointer.update <- true
		pointer.lock.Unlock()
		return job, true
	}
	pointer.lock.Unlock()
	return nil, false
}

//DelJob 删除定时器
func (pointer *Clock) DelJob(job *Job) {
	pointer.jobs.Remove(job)
	pointer.update <- true
}

//Close 关闭定时器
func (pointer *Clock) Close() {
	pointer.lock.Lock()
	pointer.isExit = exit
	ch := make(chan bool)
	pointer.exit <- ch
	<-ch
	close(pointer.update)
	close(pointer.reset)
	close(pointer.exit)
	pointer.lock.Unlock()
}

//Reset 清空定时任务
func (pointer *Clock) Reset() {
	pointer.lock.Lock()
	if pointer.isExit == exit {
		pointer.lock.Unlock()
		return
	}
	pointer.isExit = exit
	ch := make(chan bool)
	pointer.reset <- ch
	<-ch
	pointer.isExit = open
	pointer.lock.Unlock()
}

//Job 任务
type Job struct {
	clock         *Clock            //定时器
	count         int               //执行次数
	executionTime time.Time         //执行的时间
	intervalTime  time.Duration     //间隔时间
	callBackFunc  func(interface{}) //回调
	param         interface{}       //参数
}

//Cancel 取消
func (pointer *Job) Cancel() {
	pointer.clock.DelJob(pointer)
}

//GetIntervalTime 执行时间
func (pointer *Job) GetIntervalTime() time.Duration {
	return pointer.intervalTime
}

//GetTimeDifference 获取距离执行时间相差多少
func (pointer *Job) GetTimeDifference() int64 {
	return (pointer.executionTime.UnixNano() - time.Now().UnixNano()) / 1e6
}

//JobSlice 游戏切片
type JobSlice []*Job

func (s JobSlice) Len() int {
	return len(s)
}

func (s JobSlice) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func (s JobSlice) Less(i, j int) bool {
	return s[i].executionTime.UnixNano() < s[j].executionTime.UnixNano()
}

//JobQueue 任务队列
type JobQueue struct {
	jobTimes JobSlice
	lock     sync.Mutex
}

//NewJobQueue 新建
func NewJobQueue() *JobQueue {
	return &JobQueue{
		jobTimes: JobSlice{},
	}
}

//Remove 删除
func (pointer *JobQueue) Remove(job *Job) {
	pointer.lock.Lock()
	for key, vli := range pointer.jobTimes {
		if vli == job {
			pointer.jobTimes = append(pointer.jobTimes[:key], pointer.jobTimes[key+1:]...)
			break
		}
	}
	pointer.lock.Unlock()
}

//Insert 删除
func (pointer *JobQueue) Insert(job *Job) {
	pointer.lock.Lock()
	pointer.jobTimes = append(pointer.jobTimes, job)
	pointer.lock.Unlock()
}

//Min 小
func (pointer *JobQueue) Min() *Job {
	pointer.lock.Lock()
	defer pointer.lock.Unlock()
	if len(pointer.jobTimes) <= 0 {
		return nil
	}
	sort.Sort(pointer.jobTimes)
	return pointer.jobTimes[0]
}

//Clear 清空
func (pointer *JobQueue) Clear() {
	pointer.lock.Lock()
	pointer.jobTimes = JobSlice{}
	pointer.lock.Unlock()
}
